<template>
  <div class="read-box" :class="readSetting.skin">
    <!-- 封面页 -->
    <cover :bookDetail="bookDetail" v-if="windowScrolltop == 0 && isFirstPage" />

    <!-- 阅读区域 -->
    <div class="read-content">
      <div class="read-content-top">{{ currentTitle }}</div>
      <div class="read-content-list" @click="handleToOpenMenu">
        <van-pull-refresh
          v-model="refreshing"
          @refresh="loadLastChapter"
          pulling-text="下拉阅读上一章"
          loosing-text="释放阅读上一章"
        >
          <van-list
            :loading="loading"
            :finished="finished"
            :error="error"
            offset="1000"
            finished-text="没有更多了"
            error-text="请求失败，点击重新加载"
            @load="loadNextChapter"
          >
            <div
              class="read-content-item"
              v-for="item in contentList"
              :key="item.sort"
              :data-title="item.title"
              :data-index="item.sort - 1"
            >
              <div
                class="read-content-title"
                :style="{ fontSize: `${readSetting.fontSize + 4}px` }"
              >
                {{ item.title }}
              </div>
              <article
                v-html="item.content"
                class="read-content-article"
                :style="{ fontSize: `${readSetting.fontSize}px` }"
              />
            </div>
          </van-list>
        </van-pull-refresh>
      </div>
    </div>

    <!-- 设置面板 -->
    <van-popup
      :show="menuShow"
      position="top"
      :style="{ height: '7%' }"
      :overlay="false"
    >
      <van-nav-bar
        :title="bookDetail.name"
        left-text="返回"
        left-arrow
        @click-left="handleToBack"
      />
    </van-popup>
    <van-popup :show="menuShow" position="bottom" :overlay="false">
      <!-- 进度条 -->
      <div v-show="progressShow" class="progress-container">
        <div class="progress-control">
          <span @click="handleToLastChapter">上一章</span>
          <span class="van-ellipsis">{{ currentProgressTitle }}</span>
          <span @click="handleToNextChapter">下一章</span>
        </div>
        <van-slider
          active-color="#92c8ff"
          v-model="progressValue"
          @change="progressChange"
          :max="chapterList.length"
        />
      </div>
      <!-- 阅读设置 -->
      <div v-show="settingShow" class="setting-container">
        <div class="setting-control">
          <span @click="handleFontsizeMinus">A-</span>
          <van-slider
            active-color="#92c8ff"
            v-model="fontsizeValue"
            @change="fontsizeChange"
            :step="1"
            :min="14"
            :max="24"
          >
            <template #button>
              <div class="font-size">{{ fontsizeValue }}</div>
            </template>
          </van-slider>
          <span @click="handleFontsizePlus">A+</span>
        </div>
        <div class="setting-skin">
          <ul>
            <li
              v-for="item in ['skin-default', 'skin-blue', 'skin-green', 'skin-light']"
              :key="item"
              :class="readSetting.skin === item ? 'active' : ''"
              @click="handleToChooseSkin(item)"
            ></li>
          </ul>
        </div>
      </div>
      <!--面板菜单-->
      <van-grid>
        <van-grid-item @click="handleToOpenChapter">
          <svg id="icon-catalog" viewBox="0 0 16 11" class="menu-icon">
            <g>
              <path
                d="M15.464 1a.5.5 0 0 0 0-1H4a.5.5 0 0 0 0 1h11.464zM15.464 6a.5.5 0 0 0 0-1H4a.5.5 0 0 0 0 1h11.464zM15.464 11a.5.5 0 0 0 0-1H4a.5.5 0 1 0 0 1h11.464zM1 1a.5.5 0 0 0 0-1H.5a.5.5 0 1 0 0 1H1zM1.135 6a.5.5 0 1 0 0-1H.5a.5.5 0 0 0 0 1h.635zM1.135 11a.5.5 0 1 0 0-1H.5a.5.5 0 1 0 0 1h.635z"
              ></path>
            </g>
          </svg>
          <span class="menu-text">目录</span>
        </van-grid-item>
        <van-grid-item @click="handleToProgress">
          <svg id="icon-progress" viewBox="0 0 18 9" class="menu-icon">
            <g>
              <rect y="4" width="3" height="1" rx=".5"></rect>
              <rect x="14" y="4" width="4" height="1" rx=".5"></rect>
              <path
                d="M13 4.5a4.5 4.5 0 1 0-9 0 4.5 4.5 0 0 0 9 0zm-8 0a3.5 3.5 0 1 1 7 0 3.5 3.5 0 0 1-7 0z"
              ></path>
            </g>
          </svg>
          <span class="menu-text">进度</span>
        </van-grid-item>
        <van-grid-item @click="handleToSetting">
          <svg id="icon-font-set" viewBox="0 0 16 15" class="menu-icon">
            <g>
              <path
                d="M13.5 8c.942 0 1.5.465 1.5 1.5v5a.5.5 0 1 0 1 0v-1.938-.897V9.5C16 7.868 14.958 7 13.5 7c-1.369 0-2.5.754-2.5 2a.5.5 0 1 0 1 0c0-.588.619-1 1.5-1z"
              ></path>
              <path
                d="M10.495 12.5c0 1.203.748 1.951 1.93 2.12.97.138 2.19-.12 3.298-.673l-.446-.894c-.938.467-1.961.684-2.711.577-.716-.102-1.071-.458-1.071-1.13 0-1.148 1.727-1.945 3.907-1.51l.196-.98c-2.714-.542-5.103.56-5.103 2.49zM5.17 1.604a.546.546 0 0 1 .057-.134c-.036.045-.115.091-.23.09-.115 0-.19-.046-.224-.09a.54.54 0 0 1 .057.134l4.194 13.049a.5.5 0 0 0 .952-.306L5.782 1.298c-.315-.98-1.246-.988-1.564 0L.024 14.347a.5.5 0 0 0 .952.306L5.17 1.604z"
              ></path>
              <path d="M8 10V9H2v1z"></path>
            </g>
          </svg>
          <span class="menu-text">设置</span>
        </van-grid-item>
        <van-grid-item
          @click="handleToChooseSkin(lastSkin)"
          v-if="readSetting.skin == 'skin-dark'"
        >
          <svg id="icon-day" viewBox="0 0 16 16" class="menu-icon">
            <g>
              <path
                d="M11.5 8a3.5 3.5 0 1 0-7 0 3.5 3.5 0 0 0 7 0zm-6 0a2.5 2.5 0 1 1 5 0 2.5 2.5 0 0 1-5 0zM7.5.506C7.5.226 7.732 0 8 0c.276 0 .5.23.5.506v1.488c0 .28-.232.506-.5.506-.276 0-.5-.23-.5-.506V.506zm0 13.5c0-.28.232-.506.5-.506.276 0 .5.23.5.506v1.488c0 .28-.232.506-.5.506-.276 0-.5-.23-.5-.506v-1.488zM2.347 3.054a.508.508 0 0 1-.004-.71.506.506 0 0 1 .711.003L4.107 3.4a.508.508 0 0 1 .004.71.506.506 0 0 1-.711-.003L2.347 3.054zm9.546 9.546a.508.508 0 0 1-.004-.71.506.506 0 0 1 .711.003l1.053 1.053a.508.508 0 0 1 .004.71.506.506 0 0 1-.711-.003L11.893 12.6zM15.494 7.5c.28 0 .506.232.506.5 0 .276-.23.5-.506.5h-1.488A.508.508 0 0 1 13.5 8c0-.276.23-.5.506-.5h1.488zm-13.5 0c.28 0 .506.232.506.5 0 .276-.23.5-.506.5H.506A.508.508 0 0 1 0 8c0-.276.23-.5.506-.5h1.488zM12.946 2.347a.508.508 0 0 1 .71-.004.506.506 0 0 1-.003.711L12.6 4.107a.508.508 0 0 1-.71.004.506.506 0 0 1 .003-.711l1.053-1.053zM3.4 11.893a.508.508 0 0 1 .71-.004.506.506 0 0 1-.003.711l-1.053 1.053a.508.508 0 0 1-.71.004.506.506 0 0 1 .003-.711L3.4 11.893z"
              ></path>
            </g>
          </svg>
          <span class="menu-text">日间</span>
        </van-grid-item>
        <van-grid-item @click="handleToChooseSkin('skin-dark')" v-else>
          <svg id="icon-night" viewBox="0 0 15 15" class="menu-icon">
            <path
              d="M14.063 7.5A6.562 6.562 0 1 1 7.675.94L7.687.47 7.491.046a5.625 5.625 0 1 0 7.463 7.463l-.425-.196-.469.012c.002.058.002.117.002.175zm.04-.384a4.688 4.688 0 1 1-6.22-6.22C8.335.69 8.196.017 7.7.004A7.5 7.5 0 1 0 14.998 7.3c-.014-.496-.687-.635-.895-.185z"
            ></path>
          </svg>
          <span class="menu-text">夜间</span>
        </van-grid-item>
      </van-grid>
    </van-popup>
    <van-overlay z-index="100" :show="menuShow" @click="menuShow = false" />

    <!-- 章节列表 -->
    <van-popup
      :show="chapterShow"
      position="left"
      :style="{ height: '100%', width: '70%' }"
    >
      <span class="chapter-item"
        >{{ bookDetail.name }} {{ `共${chapterList.length}章` }}</span
      >
      <span
        class="chapter-item van-ellipsis"
        :id="`chapter-${index}`"
        :style="{ color: index == currentIndex ? '#92c8ff' : '' }"
        v-for="(item, index) in chapterList"
        :key="index"
        @click="handleToRead(item.book_id, item.chapter_id)"
      >
        {{ item.title }}
      </span>
    </van-popup>
  </div>
</template>
<script lang="ts" setup>
import { ref, Ref, onMounted, computed, watch } from 'vue'
import { Toast } from 'vant'
import { useRoute, useRouter } from 'vue-router'
import _ from 'lodash'
import axios from 'axios'
import { useStore } from '@/store/index'
import { getChapterDetail, getChapterAllList } from '@/api/chapter'
import { BookDetail, HistoryItem, ReadSetting } from '@/common/types'
import Cover from './components/Cover/Cover.vue'
import { getRead } from '@/api/read'

const bookDetail: Ref<any> = ref({})
const currentTitle = ref('')
const currentIndex = ref(0)
const contentList: Ref<any[]> = ref([])
const isFirstPage = computed(() => {
  return contentList.value[currentIndex.value]?.sort === 1
})

const router = useRouter()

const route = useRoute()
const init = () => {
  if (route.params.book_id && route.params.chapter_id) {
    getChapterDetail({ bookId: route.params.book_id as string })
      .then((res) => {
        bookDetail.value = res.data
      })
      .catch((error: string) => {
        Toast(error || '获取书籍信息失败')
      })
  } else {
    router.push({
      name: 'Error',
      params: {
        description: '获取书籍信息失败'
      }
    })
  }
}

let windowScrolltop: number = 0
const scrollView = () => {
  windowScrolltop = document.documentElement.scrollTop || document.body.scrollTop
  const listObserver = new IntersectionObserver((entries) => {
    entries.forEach((entry: IntersectionObserverEntry) => {
      if (entry.isIntersecting) {
        const { dataset } = entry.target as HTMLElement
        currentTitle.value = dataset.title as string
        currentIndex.value = Number(dataset.index)
      }
    })
  })
  document.querySelectorAll('.read-content-item').forEach((ele) => {
    listObserver.observe(ele)
  })
}

onMounted(() => {
  init()
  window.addEventListener('scroll', _.throttle(scrollView, 500), false)
})

const refreshing = ref(false)
const loading = ref(false)
const finished = ref(false)
const error = ref(false)
const last_chapterId: Ref<undefined | number> = ref(undefined)
const next_chapterId: Ref<undefined | number> = ref(undefined)
const menuShow = ref(false)
const chapterShow = ref(false)
const chapterLoading = ref(false)
const chapterFinished = ref(false)
const chapterList: Ref<any[]> = ref([])
const progressShow = ref(false)
const progressValue = ref(0)
const currentProgressTitle = ref('')
const settingShow = ref(false)
const fontsizeValue = ref(0)
const pageScrollY = ref(0)
const max = ref(0)

const store = useStore()
const lastSkin: Ref<string> = computed(() => {
  return store.state.readSettingModule.lastSkin
})

const readSetting: Ref<ReadSetting> = computed(() => {
  return store.state.readSettingModule.readSetting
})

const updateHistoryRead = (val: any) => {
  const historyRead: HistoryItem = {
    name: bookDetail.value.name,
    book_id: bookDetail.value.id,
    image: bookDetail.value.image,
    writer: bookDetail.value.writer,
    type: bookDetail.value.type,
    is_over: bookDetail.value.is_over,
    chapter_id: val.id,
    chapter_name: val.title,
    read_time: new Date().getTime()
  }
  store.dispatch('historyReadModule/addHistoryRead', historyRead)
}

watch(
  currentIndex,
  async (val) => {
    if (chapterList.value.length === 0) {
      const { data } = await getChapterAllList({
        bookId: `${route.params.book_id}`,
        index: 1,
        pageSize: 50,
        type: 'next'
      })
      chapterList.value = data.list
    }
    updateHistoryRead(chapterList.value[val])
  },
  { immediate: true }
)

const handleToOpenMenu = (e: MouseEvent) => {
  const safeHeight: number = 50
  const windowScrollHeight: number =
    document.documentElement.scrollHeight || document.body.scrollHeight
  const windowHeight: number = window.screen.height
  const windowOffsetY: number = window.pageYOffset
  const offsetY: number = windowHeight / 3
  const currentY: number = e.clientY
  if (currentY <= offsetY) {
    const scrollY: number = windowOffsetY - windowHeight + safeHeight
    window.scrollTo(0, scrollY < 0 ? 0 : scrollY)
  } else if (offsetY <= currentY && currentY <= offsetY * 2) {
    menuShow.value = !menuShow.value
  } else if (currentY >= offsetY * 2 && currentY <= offsetY * 3) {
    const scrollY: number = windowOffsetY + windowHeight - safeHeight
    window.scrollTo(0, scrollY > windowScrollHeight ? windowScrollHeight : scrollY)
  }
}

const getReadContent = (url: string) => {
  return new Promise((resolve, reject) => {
    axios
      .get(url)
      .then((res) => {
        resolve(res.data.replace(/#{3}.*?#{3}/g, ''))
      })
      .catch((e) => {
        reject(e)
      })
  })
}

const loadLastChapter = async () => {
  if (last_chapterId.value === 0) {
    Toast('已经是第一章了哦')
    refreshing.value = false
    return
  }
  const { data } = await getRead({
    bookId: `${route.params.book_id}`,
    chapterId: `${last_chapterId.value}`
  })
  const readContent = await getReadContent(data.path)
  data.content = readContent
  last_chapterId.value = data.last_chapterId
  contentList.value.unshift(data)
  scrollView()
  refreshing.value = false
}
const loadNextChapter = async () => {
  try {
    loading.value = true
    if (next_chapterId.value === 0) {
      Toast('已经是最后一章了哦')
      finished.value = true
      return
    }
    next_chapterId.value =
      next_chapterId.value !== undefined
        ? next_chapterId.value
        : Number(route.params.chapter_id)
    const { data } = await getRead({
      bookId: `${route.params.book_id}`,
      chapterId: `${next_chapterId.value}`
    })
    const readContent = await getReadContent(data.path)
    data.content = readContent
    last_chapterId.value =
      last_chapterId.value !== undefined ? last_chapterId.value : data.last_chapterId
    next_chapterId.value = data.next_chapterId
    contentList.value.push(data)
    loading.value = false
  } catch (e) {
    if (e === 'Error: Invalid parameter') {
      router.push({
        name: 'Error',
        params: {
          description: '很抱歉，此章节不存在或已删除'
        }
      })
    }
    loading.value = false
    error.value = true
  }
}

const handleToLastChapter = async () => {
  const { data } = await getRead({
    bookId: `${route.params.book_id}`,
    chapterId: `${last_chapterId.value}`
  })
  const readContent = await getReadContent(data.path)
  data.content = readContent
  currentProgressTitle.value = data.title
  last_chapterId.value = data.last_chapterId
  next_chapterId.value = data.next_chapterId
  contentList.value = [data]
  window.scrollTo(0, 0)
}
const handleToNextChapter = async () => {
  const { data } = await getRead({
    bookId: `${route.params.book_id}`,
    chapterId: `${next_chapterId.value}`
  })
  const readContent = await getReadContent(data.path)
  data.content = readContent
  currentProgressTitle.value = data.title
  last_chapterId.value = data.last_chapterId
  next_chapterId.value = data.next_chapterId
  contentList.value = [data]
  window.scrollTo(0, 0)
}

const progressChange = async () => {
  const currentChapter: any = chapterList.value[progressValue.value]
  currentProgressTitle.value = currentChapter.title
  currentTitle.value = currentChapter.title
  currentIndex.value = progressValue.value
  const { data } = await getRead({
    bookId: currentChapter.book_id,
    chapterId: currentChapter.id
  })
  last_chapterId.value = data.last_chapterId
  next_chapterId.value = data.next_chapterId
  contentList.value = [data]
  window.scrollTo(0, 0)
}

const handleToSetting = () => {
  fontsizeValue.value = readSetting.value.fontSize
  settingShow.value = !settingShow.value
}

const fontsizeChange = () => {
  const setting: ReadSetting = {
    skin: readSetting.value.skin,
    fontSize: fontsizeValue.value
  }
  store.dispatch('readSettingModule/saveReadSetting', setting)
}

const handleFontsizeMinus = () => {
  if (fontsizeValue.value > 14) {
    fontsizeValue.value -= 1
    fontsizeChange()
  }
}
const handleFontsizePlus = () => {
  if (fontsizeValue.value < 24) {
    fontsizeValue.value += 1
    fontsizeChange()
  }
}

const handleToChooseSkin = (skin: string) => {
  const setting: ReadSetting = {
    skin,
    fontSize: readSetting.value.fontSize
  }
  store.dispatch('readSettingModule/saveReadSetting', setting).then(() => {
    Toast.success('切换成功')
  })
}

const handleToOpenChapter = async () => {
  try {
    if (chapterList.value.length === 0) {
      const { data } = await getChapterAllList({
        bookId: `${route.params.book_id}`,
        index: 1,
        pageSize: 50,
        type: 'next'
      })
      chapterList.value = data.list
    }
    menuShow.value = false
    chapterShow.value = true
    setTimeout(() => {
      const currentChapter: Element = document.querySelector(
        `#chapter-${currentIndex.value}`
      ) as Element
      currentChapter.scrollIntoView({
        behavior: 'smooth'
      })
    }, 500)
  } catch (e) {
    Toast(e as string)
  }
}

const handleToProgress = async () => {
  if (!progressShow.value) {
    if (chapterList.value.length === 0) {
      const { data } = await getChapterAllList({
        bookId: `${route.params.book_id}`,
        index: 1,
        pageSize: 50,
        type: 'next'
      })
      chapterList.value = data.list
      max.value = chapterList.value.length
    }
    currentProgressTitle.value = chapterList.value[currentIndex.value].title
    progressValue.value = currentIndex.value
  }
  progressShow.value = !progressShow.value
}

const handleToRead = (book_id: string, chapter_id: string) => {
  if (chapterList.value.length === 0) {
    Toast('未找到章节列表,不能阅读该书!')
    return
  }
  router.replace({
    name: 'Read',
    params: {
      book_id,
      chapter_id
    }
  })
}

const handleToBack = () => {
  router.back()
}
</script>
<style lang="scss" scoped>
$defaultSkinColor: #c4b395;
$blueSkinColor: #cad9e8;
$greenSkinColor: #d1edd1;
$lightSkinColor: #e6e6e6;
$darkSkinColor: #1a1a1a;
.read-box {
  min-height: 100vh;
  .read-content {
    .read-content-top {
      position: fixed;
      top: 0;
      right: 0;
      left: 0;
      padding: 10px;
      background: #fff;
      z-index: 10;
      color: rgba(0, 0, 0, 0.4);
      font-size: 14px;
      font-weight: 400;
    }
    .read-content-list {
      padding: 50px 20px 0 20px;
      z-index: 9;
      .read-content-item {
        .read-content-title {
          font-size: 22px;
          font-weight: 700;
        }
      }
    }
  }
  .progress-container {
    padding: 20px;
    .progress-control {
      display: flex;
      justify-content: space-between;
      padding: 0 0 20px 0;
      width: 100%;
      span {
        white-space: nowrap;
      }
    }
  }
  .setting-container {
    .setting-control {
      display: flex;
      justify-content: space-between;
      align-items: center;
      padding: 20px 0;
      width: 100%;
      .font-size {
        width: 26px;
        color: #fff;
        font-size: 10px;
        line-height: 18px;
        text-align: center;
        background-color: #92c8ff;
        border-radius: 100px;
      }
      span {
        white-space: nowrap;
        width: 100px;
        text-align: center;
      }
    }
    .setting-skin {
      li {
        display: inline-block;
        width: 21%;
        height: 30px;
        margin: 2%;
        border-radius: 4px;
        &:nth-child(1) {
          background: $defaultSkinColor;
        }
        &:nth-child(2) {
          background: $blueSkinColor;
        }
        &:nth-child(3) {
          background: $greenSkinColor;
        }
        &:nth-child(4) {
          background: $lightSkinColor;
        }
      }
      .active {
        position: relative;
        &::after {
          content: '';
          height: 17px;
          width: 2px;
          border-radius: 10px;
          display: block;
          background: #ff003f;
          position: absolute;
          top: 6px;
          left: 39px;
          transform: rotate(45deg);
        }
        &:before {
          content: '';
          height: 8px;
          width: 2px;
          background: #ff003f;
          transform: rotate(-45deg);
          position: absolute;
          top: 12px;
          left: 30px;
        }
      }
    }
  }
  .chapter-item {
    position: relative;
    display: block;
    font-size: 14px;
    color: #323233;
    padding: 10px 16px;
    &::after {
      position: absolute;
      box-sizing: border-box;
      content: ' ';
      pointer-events: none;
      right: 16px;
      bottom: 0;
      left: 16px;
      border-bottom: 1px solid #ebedf0;
      -webkit-transform: scaleY(0.5);
      transform: scaleY(0.5);
    }
  }
}

.menu-icon {
  height: 16px;
  width: 16px;
}

.menu-text {
  font-size: 12px;
  color: #646566;
  margin-top: 4px;
}

.skin-default {
  background: $defaultSkinColor;
  .read-content {
    .read-content-top {
      background: $defaultSkinColor;
    }
  }
}

.skin-blue {
  background: $blueSkinColor;
  .read-content {
    .read-content-top {
      background: $blueSkinColor;
    }
  }
}

.skin-green {
  background: $greenSkinColor;
  .read-content {
    .read-content-top {
      background: $greenSkinColor;
    }
  }
}

.skin-light {
  background: $lightSkinColor;
  .read-content {
    .read-content-top {
      background: $lightSkinColor;
    }
  }
}

.skin-dark {
  background: $darkSkinColor;
  .read-content {
    color: rgba(255, 255, 255, 0.5);
    .read-content-top {
      color: rgba(255, 255, 255, 0.5);
      background: $darkSkinColor;
    }
  }
}
</style>
